Skip to content

chore(deps): Bump @langchain/core 1.1.48 + @langchain/google-* 2.1.31, with Anthropic tool-input compat fix#232

Merged
danny-avila merged 5 commits into
mainfrom
claude/bump-langchain-google
Jun 9, 2026
Merged

chore(deps): Bump @langchain/core 1.1.48 + @langchain/google-* 2.1.31, with Anthropic tool-input compat fix#232
danny-avila merged 5 commits into
mainfrom
claude/bump-langchain-google

Conversation

@danny-avila

@danny-avila danny-avila commented Jun 9, 2026

Copy link
Copy Markdown
Owner

Summary

Bumps the Google/LangChain stack to the latest patch releases and includes the Anthropic compatibility fix required to keep tool calling working under the new @langchain/core:

  • @langchain/core 1.1.441.1.48
  • @langchain/google-common / google-gauth / google-genai / google-vertexai 2.1.282.1.31
  • @anthropic-ai/sandbox-runtime0.0.54 (npm audit alignment)
  • fix: Anthropic streamed tool-input aggregation under @langchain/core ≥ 1.1.46 (see below)

The overrides $-references follow the dependencies versions automatically. Lockfile regenerated.

Anthropic tool-input compatibility fix

@langchain/core ≥ 1.1.46 changed streaming content-block aggregation ("keep different block types separate when merging chunks" + "v1 message casting"). For Anthropic tool calls this now leaves the tool_use content block's input empty — the streamed input is split into a separate input_json_delta block (v1-cast to a stray text block). The assembled arguments still live on message.tool_calls, but every consumer that reads tool input from message.content breaks:

  • _formatContent re-serialization → Unsupported message content format (multi-turn tool calls, summarization).
  • @langchain/anthropic's AnthropicToolsOutputParser, used by withStructuredOutput (e.g. title generation) → OUTPUT_PARSING_FAILURE on empty input.

Fixed in two layers, without disturbing the fork's Vertex/GCP/Azure-specific handling:

  1. Source — utils/message_outputs.ts: emit the partial tool input as an untyped { index, input } block so core merges it back into whichever block sits at that index (tool_use or server_tool_use), restoring the input in message.content. Fixes all content-reading consumers at the source.
  2. Defensive — utils/message_inputs.ts _formatContent: restore an emptied tool_use input from message.tool_calls, falling back to merging sibling input_json_delta blocks by index; drop orphaned delta / text-with-input blocks instead of throwing. Covers persisted/legacy messages.

Tests

  • Unit (utils/streaming-tool-input.test.ts): orphan-skip, restore-from-tool_calls, no-overwrite, sibling-delta merge, and type-less chunk emission + aggregation.
  • Live (llm.spec.ts): "converting messages doesn't drop tool input".
  • The sibling-merge and live tests are ported from @langchain/anthropic's own regression suite.

Why core and google-* must move together

@langchain/google-common@2.1.29 (PR #10776) switched function-call id generation back to importing v4 from @langchain/core/utils/uuid (2.1.28 imported it from standalone uuid). That import path is what crashed in LibreChat#13003(0, _langchain_core_utils_uuid.v4) is not a function — due to a Rolldown CJS emit bug in older @langchain/core.

@langchain/core@1.1.48 (PR #10832, "make CJS default re-exports callable") fixes that emit. Verified:

require('@langchain/core/utils/uuid').v4()  // -> '9aac90b4-…' (callable ✅; non-callable namespace object in 1.1.44)

So the revert is safe only when core is also ≥ 1.1.48. Note google-common@2.1.31 only declares core ^1.1.47, but the callable-export fix landed in 1.1.48 — so core is pinned to an exact 1.1.48 here, not a ^ range.

Changelog breakdown (current → bumped)

@langchain/core 1.1.44 → 1.1.48 — 1.1.45 deprecations + Deno fix · 1.1.46 inline ansi-styles/camelcase/decamelize, keep distinct content-block types separate when merging · 1.1.47 add UUID v6, v1 casting after streaming aggregation · 1.1.48 fix(core): make CJS default re-exports callable (#10832; the #13003 root cause), OpenRouter reasoning v1 blocks, OpenAI Responses custom-tool streaming.

@langchain/google-common 2.1.28 → 2.1.31 — 2.1.29 uuid-vuln remediation (reverts to core uuid import, #10776) · 2.1.30 structured output parsing with includeRaw+reasoning · 2.1.31 fix: missing stream usage metadata on Gemini (#9912).

google-genai / google-gauth / google-vertexai — uuid remediation (genai) + dependency passthroughs.

What this does NOT do

Does not address the Gemini union-type schema throw (zod_to_gemini_parameters: Gemini cannot handle union types, langchainjs#9691) — still open upstream and unchanged in google-common@2.1.31. That case is handled downstream in LibreChat by flattening union schemas before they reach the Gemini converter.

Validation

  • Live (against the real Anthropic API): structured-output title generation, parallel web_search (server tool) + calculator (regular tool), tool round-trips / follow-ups, plus the new tests — all pass.
  • npm install clean (no peer-dependency conflicts); npm run build (rollup + tsc) clean.
  • CI green (unit shards, typecheck, lint, change-gated live integration).

…o 2.1.31

Routine maintenance bump of the Google/LangChain stack:
- @langchain/core 1.1.44 -> 1.1.48
- @langchain/google-common / google-gauth / google-genai / google-vertexai 2.1.28 -> 2.1.31

These must move together: google-common 2.1.31 imports `v4` from
`@langchain/core/utils/uuid` (reverting 2.1.28's standalone-`uuid` import),
which is the same path that crashed in LibreChat#13003. core 1.1.48 fixes the
Rolldown CJS emit so that export resolves to a callable function again
(verified: `require('@langchain/core/utils/uuid').v4()` returns a UUID), so the
crash does not recur. Bumping google-common alone (core pinned at 1.1.44) would
reintroduce it.

Build (rollup + tsc) passes. Does not address the Gemini union-type schema
throw (langchain-ai/langchainjs#9691, still open) — that is handled downstream
in LibreChat.
danny-avila added a commit to danny-avila/LibreChat that referenced this pull request Jun 9, 2026
The union flatten alone wasn't enough — real GitHub MCP tools on Gemini also 400
with `Invalid value ... (TYPE_STRING), true`, because Gemini's function-calling
Schema (https://ai.google.dev/api/caching#Schema) accepts only a restricted JSON
Schema subset, and `enum` is `Type.STRING`-only.

Rename `flattenJsonSchemaUnions` → `sanitizeGeminiSchema` and broaden it (one pass,
Gemini-gated) to cover the documented subset:

- Keep only string `enum` values; drop the keyword for non-string types (fixes the
  reported boolean-enum 400, incl. boolean `const` normalized to `enum: [true]`).
- `const` → single-value string enum, or drop if non-string.
- Merge `allOf` intersections; fold `exclusiveMinimum`/`exclusiveMaximum` into
  `minimum`/`maximum`.
- Strip unsupported keywords: `additionalProperties`, `default`, `$schema`, `$id`.
- (Existing) collapse `anyOf`/`oneOf`, multi-entry `type` arrays, nullable.

Grounded in Google's Schema docs rather than reverse-engineered from 400s. Verified
end-to-end against the real `@langchain/google-common` converter. Complements
danny-avila/agents#232 (langchain bump), which defers schema flattening to LibreChat.
…egation

@langchain/core >= 1.1.46 changed streaming content-block aggregation ("keep
different block types separate" + "v1 message casting"). For Anthropic tool
calls it now keeps the input_json_delta as a separate content block, v1-casts it
to a `text` block carrying `input` but no `text`, and leaves the sibling
tool_use block with an empty inline input. Re-serializing such a message threw
"Unsupported message content format" in _formatContent.

Align _formatContent with upstream @langchain/anthropic's handling, without
disturbing the fork's Vertex/GCP/Azure-specific corrections:
- restore an emptied tool_use input from the matching message.tool_calls args
- drop orphaned input_json_delta blocks and the v1-cast `text`-with-`input`
  variant instead of throwing

Add a regression test for the aggregated-streaming tool-input scenario.
…ges them

Root-cause fix for the core >= 1.1.46 aggregation change. _makeMessageChunkFromAnthropicEvent
emitted the input_json_delta as a separate content block typed `input_json_delta`;
core now keeps differently-typed blocks separate, leaving the sibling tool_use
block's input empty and the delta orphaned. Every consumer that reads tool input
from message.content then breaks — re-serialization (_formatContent) and, e.g.,
@langchain/anthropic's AnthropicToolsOutputParser used by withStructuredOutput
(title generation), which threw OUTPUT_PARSING_FAILURE on empty input.

Emit the partial input as an untyped `{ index, input }` block so core merges it
into whichever block (tool_use or server_tool_use) sits at that index, restoring
the assembled input in message.content. Fixes all content-reading consumers at
the source; the message_inputs defensive handling stays as a safety net.

Add a unit test covering chunk emission + aggregation.
…am tool tests

_formatContent now restores an emptied tool_use input from sibling
input_json_delta blocks (by index) when there is no matching message.tool_calls
entry — aligning with @langchain/anthropic and covering persisted messages that
predate the chunk-emission fix.

Port two upstream @langchain/anthropic regression tests that guard this path:
- "partial tool input is correctly merged before calling Anthropic API" (unit)
- "converting messages doesn't drop tool input" (live), adapted to CustomAnthropic
danny-avila added a commit to danny-avila/LibreChat that referenced this pull request Jun 9, 2026
)

* 🧰 fix: Flatten union schemas for Gemini/Vertex MCP tool compatibility

`@langchain/google-common`'s `zod_to_gemini_parameters` throws "Gemini cannot
handle union types" on any genuine `anyOf`/`oneOf` (e.g. discriminated unions),
so MCP tools shipping union-typed schemas crash on the Google endpoint while
working fine on OpenAI/Claude.

Add `flattenJsonSchemaUnions` (packages/api) to collapse unions to their first
non-null member and multi-entry `type` arrays to a single nullable type, and
apply it in `createToolInstance`'s existing `isGoogle` branch so only the
Google/Vertex path is affected. Lossy by design, mirroring the existing
empty-object fallback.

Closes #13612

* 🩹 fix: Address Codex review — preserve fields, strip null enums, cover definitions path

- Preserve parent-level `properties`/`required` when collapsing a union: merge the
  chosen branch into the parent instead of overwriting, so args declared outside the
  union (e.g. always-required fields) still reach Gemini.
- Drop the `null` member from `enum` when a union/type-array makes a field nullable,
  keeping Gemini's required homogeneous-enum invariant.
- Propagate the Google-flattened schema to the definitions/deferred-tool path:
  thread `provider` into `loadToolDefinitions` and flatten there, and store the
  flattened schema on `mcpJsonSchema` so `extractMCPToolDefinition` no longer emits
  raw unions on Google/Vertex.

* 🎨 style: Sort imports in tools/definitions per import-order check

* ♊ feat: Broaden union flatten into a full Gemini schema sanitizer

The union flatten alone wasn't enough — real GitHub MCP tools on Gemini also 400
with `Invalid value ... (TYPE_STRING), true`, because Gemini's function-calling
Schema (https://ai.google.dev/api/caching#Schema) accepts only a restricted JSON
Schema subset, and `enum` is `Type.STRING`-only.

Rename `flattenJsonSchemaUnions` → `sanitizeGeminiSchema` and broaden it (one pass,
Gemini-gated) to cover the documented subset:

- Keep only string `enum` values; drop the keyword for non-string types (fixes the
  reported boolean-enum 400, incl. boolean `const` normalized to `enum: [true]`).
- `const` → single-value string enum, or drop if non-string.
- Merge `allOf` intersections; fold `exclusiveMinimum`/`exclusiveMaximum` into
  `minimum`/`maximum`.
- Strip unsupported keywords: `additionalProperties`, `default`, `$schema`, `$id`.
- (Existing) collapse `anyOf`/`oneOf`, multi-entry `type` arrays, nullable.

Grounded in Google's Schema docs rather than reverse-engineered from 400s. Verified
end-to-end against the real `@langchain/google-common` converter. Complements
danny-avila/agents#232 (langchain bump), which defers schema flattening to LibreChat.

* 🩹 fix: Gate enum retention on the effective (collapsed) type

Codex review: a mixed-type enum like `type: ['integer','string'], enum: [1,'auto']`
collapsed the type to `integer` but still kept the string value `'auto'`, yielding
`{type:'integer', enum:['auto']}` — a non-string type with an enum, which Gemini
rejects. Keep `enum` only when the effective collapsed type is string (or unset),
and stamp `type: 'string'` on a surviving typeless enum (e.g. a string `const`
discriminator) so it satisfies Gemini's Type.STRING enum requirement.
@danny-avila danny-avila changed the title chore(deps): Bump @langchain/core to 1.1.48 and @langchain/google-* to 2.1.31 chore(deps): Bump @langchain/core to 1.1.48 and @langchain/google-* to 2.1.31 Jun 9, 2026
@danny-avila danny-avila changed the title chore(deps): Bump @langchain/core to 1.1.48 and @langchain/google-* to 2.1.31 chore(deps): Bump @langchain/core 1.1.48 + google-* 2.1.31, with Anthropic tool-input compat fix Jun 9, 2026
@danny-avila danny-avila changed the title chore(deps): Bump @langchain/core 1.1.48 + google-* 2.1.31, with Anthropic tool-input compat fix chore(deps): Bump @langchain/core 1.1.48 + google-* 2.1.31, with Anthropic tool-input compat fix Jun 9, 2026
@danny-avila danny-avila changed the title chore(deps): Bump @langchain/core 1.1.48 + google-* 2.1.31, with Anthropic tool-input compat fix chore(deps): Bump @langchain/core 1.1.48 + @langchain/google-* 2.1.31, with Anthropic tool-input compat fix Jun 9, 2026
@danny-avila danny-avila merged commit 5cce566 into main Jun 9, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant